﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Duellum.Game;
using Duellum.Map;
using Duellum.Wpf2d.Plugin;
using JpLabs.Geometry;
using JpLabs.Extensions;
using Duellum.Wpf2d.Plugin.Drawing;
using System.Collections.Generic;

namespace Duellum.Wpf.Play
{
	/// <summary>
	/// Interaction logic for GamePage.xaml
	/// </summary>
	public partial class GamePage : Page
	{
		private MapCursorRenderer currentPlayerCursor;
		private MapCursorRenderer highlightCursor;
		private Stack<PointInt> mouseClickPath;

		public GamePage()
		{
			InitializeComponent();
			mapViewer.DuelDomain = App.Current.CurrentDomain;
		}

		public GamePage(DuelGame game)
		{
			InitializeComponent();
			mapViewer.DuelDomain = App.Current.CurrentDomain;
			
			this.Game = game;
			
			CreateCursors();
		}

		public DuelGame Game
		{
			get; private set;
		}
		
		public Wpf2dPlayer CurrentPlayer
		{
			get { return (Wpf2dPlayer)Game.CurrentPlayer; }
		}
		
		private void CreateCursors()
		{
			//Create mouse cursor
			{
				var mouseCursorRenderer = new MapMouseCursorRenderer(new MapCursorDrawing(Colors.Red, 2), MapZOrder.Medium);
				mapViewer.AddRenderer(mouseCursorRenderer);
			}

			//Create highlight cursor
			{
				this.highlightCursor = new MapCursorRenderer(
					new MapCursorDrawing(Colors.Blue, 3, 1),
					MapZOrder.Medium + 1
				);
				mapViewer.AddRenderer(highlightCursor);
				
				//selectedCursor.PositionChanged	+= SelectedCursor_PositionChanged;
				mapViewer.LevelChanged			+= MapViewer_LevelChanged;
				mapViewer.VisualHost.MouseDown	+= MapVisualHost_MouseDown;
				mapViewer.VisualHost.MouseMove	+= MapVisualHost_MouseMove;
				mapViewer.VisualHost.MouseClick	+= MapVisualHost_MouseClick;
			}
			
			//Current player cursor
			{
				currentPlayerCursor = new MapCursorRenderer(
					new MapCursorDrawing(Colors.Orange, 4, -3),
					//new BasePlayerDrawing(Colors.OrangeRed),
					MapZOrder.Bottom
				);
				mapViewer.AddRenderer(currentPlayerCursor);
			}
		}

		private void MapViewer_LevelChanged(object sender, RoutedPropertyChangedEventArgs<int> e)
		{
			highlightCursor.Position = null;
		}
		
		private void RecordMouseClickPath(PointInt mousePosition)
		{
			if (mouseClickPath == null) mouseClickPath = new Stack<PointInt>();
			if (mouseClickPath.Count == 0 || mouseClickPath.Peek() != mousePosition) mouseClickPath.Push(mousePosition);
		}
		
		private void MapVisualHost_MouseDown(object sender, MouseButtonEventArgs e)
		{
			var mousePosition = Wpf2dPlugin.PointToPosition(e.GetPosition((IInputElement)sender));
			RecordMouseClickPath(mousePosition);

			highlightCursor.Position = mousePosition;
		}

		private void MapVisualHost_MouseMove(object sender, MouseEventArgs e)
		{
			if (mouseClickPath == null) return; //don't record path while no button is pressed
			
			var mousePosition = Wpf2dPlugin.PointToPosition(e.GetPosition((IInputElement)sender));
			RecordMouseClickPath(mousePosition);

			highlightCursor.Position = mousePosition;
		}

		private void MapVisualHost_MouseClick(object sender, MouseButtonEventArgs e)
		{
			var mousePosition = Wpf2dPlugin.PointToPosition(e.GetPosition((IInputElement)sender));
			
			RecordMouseClickPath(mousePosition);

			highlightCursor.Position = null;
			
			var clickPath = mouseClickPath.Reverse().ToArray();
			mouseClickPath = null;
			
			switch (e.ChangedButton) {
				case MouseButton.Right:	OnRightClick(clickPath, Keyboard.Modifiers); break;
				case MouseButton.Left:	OnLeftClick(clickPath, Keyboard.Modifiers); break;
			}
		}

		private void OnRightClick(IList<PointInt> clickPath, ModifierKeys modifierKeys)
		{
			if (clickPath.Count > 1) return; //No dragging is supported yet

			if (Game.GameState == DuelGameState.Playing)
			{
				PointInt mousePosition = clickPath.Last();
				var turnCmd = Game.GetCommand(DuelCoreCommands.Turn);
				
				//TODO: somehow, show some kind of message explaining why player can't Turn
				if (turnCmd == null || !turnCmd.CanExecute(mousePosition)) return;
				
				turnCmd.Execute(mousePosition);
				
				//TODO: (or not) find a way to invalidate a single object
				mapViewer.InvalidateMapVisual();
			}
		}

		private void OnLeftClick(IList<PointInt> clickPath, ModifierKeys modifierKeys)
		{
			if (clickPath.Count > 1) return; //No dragging is supported yet
			
			if (Game.GameState == DuelGameState.Playing)
			{
				PointInt mousePosition = clickPath.Last();
				var moveCmd = Game.GetCommand(DuelCoreCommands.Move);
				
				//TODO: somehow, show some kind of message explaining why player can't Move
				if (moveCmd == null || !moveCmd.CanExecute(mousePosition)) return;
				
				moveCmd.Execute(mousePosition);
				
				//TODO: make this binding automatic
				currentPlayerCursor.Position = Game.CurrentPlayer.Position;
								
				//TODO: (or not) find a way to invalidate a single object
				mapViewer.InvalidateMapVisual();
			}
		}

		private void Page_Loaded(object sender, RoutedEventArgs e)
		{
			//execute mapviewer command bindings even when it's not focused
			this.CommandBindings.AddRange(mapViewer.CommandBindings);
			
			if (this.Game != null)
			{
			    //this.mapViewer.Map = Game.Map;
			    //lblMessage.Text = "Initializing...";
			    
				//System.Threading.Thread.Sleep(5000);
			    this.Game.Initialize(true);
			    
			    var message = string.Format(
					"Game Initialized\n Player Order: {0}",
					string.Join(", ", Game.GetPlayerQueue().Select( p => ((Wpf2dPlayer)p).Name ).ToArray())
			    );
			    
			    ShowMessage(message, "Start Game", btnOk_Click_StartGame);
			}
		}
		
		private void ShowMessage(string message, string okText, RoutedEventHandler okClick)
		{
			HideGame();
			
			pnlMessageArea.Visibility = Visibility.Visible;
		    lblMessage.Text = message;
			btnOk.Content = okText;
			btnOk.Focus();
			
			RoutedEventHandler handler = null;
			handler = (object sender, RoutedEventArgs e) =>
			{
				btnOk.Click -= handler;
				pnlMessageArea.Visibility = Visibility.Collapsed;
				
				okClick(sender, e);
			};
			btnOk.Click += handler;
		}

		private void btnOk_Click_StartGame(object sender, RoutedEventArgs e)
		{
			Game.StartFirstTurn();
			
			var player = (Wpf2dPlayer)Game.CurrentPlayer;
			var message = string.Format("{0}, please seat", player.Name);
			ShowMessage(message, "Start Turn", btnOk_Click_StartTurn);
		}

		private void btnOk_Click_StartTurn(object sender, RoutedEventArgs e)
		{
			Game.StartTurn();

			ShowGame();
		}
		
		private void ShowGame()
		{
			pnlGame.DataContext = Game;
			pnlGame.Visibility = Visibility.Visible;
			
			var playerPos = CurrentPlayer.Position.Value;
			mapViewer.FocusAt(playerPos);

			//((BasePlayerDrawing)currentPlayerCursor.Drawing).SetColor(CurrentPlayer.Color);
			currentPlayerCursor.Position = playerPos;

			mapViewer.Focus();
		}

		private void HideGame()
		{
			pnlGame.DataContext = null;
			pnlGame.Visibility = Visibility.Collapsed;
			
			currentPlayerCursor.Position = null;
		}

		private void btnEndTurn_Click(object sender, RoutedEventArgs e)
		{
			if (Game.GameState != DuelGameState.Playing) return;
			
			//TODO: spend all player's TU before ending turn (alternative: reserve part (half?) of TU for the next turn)
			
			Game.EndTurn();

			var player = (Wpf2dPlayer)Game.CurrentPlayer;
			var message = string.Format("{0}, please seat", player.Name);
			ShowMessage(message, "Start Turn", btnOk_Click_StartTurn);
		}

		private void btnCenter_Click(object sender, RoutedEventArgs e)
		{
			if (Game.GameState != DuelGameState.Playing) return;
			
			mapViewer.FocusAt(CurrentPlayer.Position.Value);
		}
	}
}
